<?php
/**
 * AliyunOSSUploader 是一个用于上传文件到阿里云对象存储服务（OSS）
 * 使用OSS REST API，无需额外的库。
 */
class AliyunOSSUploader {
    /**
     * @var string 阿里云OSS服务的终端节点URL。
     */
    private $endpoint;

    /**
     * @var string 由阿里云提供的Access Key ID。
     */
    private $accessKeyId;

    /**
     * @var string 由阿里云提供的Access Key Secret。
     */
    private $accessKeySecret;

    /**
     * @var string Bucket 域名。⚠️：直接使用Endpoint提供的，不需要带Bucket 名称
     */
    private $bucket;

    /**
     * 构造函数
     */
    public function __construct($config) {
        $this->accessKeyId = $config['accessKeyId'];
        $this->accessKeySecret = $config['accessKeySecret'];
        $this->bucket = $config['bucket'];
        $this->endpoint = $config['endpoint'];
    }

    /**
     * 生成签名。
     * @param string $method HTTP方法（如"PUT"）。
     * @param string $contentMd5 内容的MD5摘要。
     * @param string $contentType 内容类型。
     * @param string $date 日期。
     * @param string $resource 资源路径。
     * @return string 返回完整的授权头。
     */
    private function generateSignature(string $method, string $contentMd5, string $contentType, string $date, string $resource): string
    {
        $stringToSign = "{$method}\n{$contentMd5}\n{$contentType}\n{$date}\n{$resource}";
        $signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true));
        return "OSS {$this->accessKeyId}:{$signature}";
    }

    /**
     * 发送HTTP请求。
     * @param string $url 请求的URL。
     * @param string $method HTTP方法。
     * @param array $headers 请求头部。
     * @param string|null $body 请求体内容。
     * @return bool 返回请求是否成功。
     */
    private function sendRequest(string $url, string $method, array $headers, string $body = null): bool
    {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        if ($body !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return $httpCode == 200;
    }

    /**
     * 上传文件到OSS。
     * @param string $filePath 本地文件路径。
     * @param string $objectName 在OSS中的对象名。
     * @return bool 返回上传是否成功。
     */
    public function uploadFile(string $filePath, string $objectName): bool
    {
        $content = file_get_contents($filePath);
        $contentMd5 = base64_encode(md5($content, true));
        $contentType = mime_content_type($filePath);
        $date = gmdate('D, d M Y H:i:s \G\M\T');
        $resource = "/{$this->bucket}/{$objectName}";
        $authHeader = $this->generateSignature("PUT", $contentMd5, $contentType, $date, $resource);

        $url = "https://{$this->bucket}.{$this->endpoint}/{$objectName}";
        $headers = [
            'Authorization: ' . $authHeader,
            'Date: ' . $date,
            'Content-Type: ' . $contentType,
            'Content-MD5: ' . $contentMd5
        ];
        return $this->sendRequest($url, "PUT", $headers, $content);
    }

    /**
     * 获取并返回指定对象的内容。
     * @param string $objectName 在OSS中的对象名。
     * @return mixed 文件内容或者在失败时返回false。
     */
    public function getFileContent($objectName) {
        $date = gmdate('D, d M Y H:i:s \G\M\T');
        $resource = "/{$this->bucket}/{$objectName}";
        $authHeader = $this->generateSignature("GET", "", "", $date, $resource);
        $url = "https://{$this->bucket}.{$this->endpoint}/{$objectName}";
        $headers = [
            'Authorization: ' . $authHeader,
            'Date: ' . $date
        ];
        return $this->sendRequest($url, "GET", $headers);
    }

    /**
     * 下载指定对象到本地文件系统。
     * @param string $objectName 在OSS中的对象名。
     * @param string $saveAsPath 本地文件保存路径。
     * @return bool 成功返回true，失败返回false。
     * @throws Exception
     */
    public function downloadFile($objectName, $saveAsPath): bool
    {
        $date = gmdate('D, d M Y H:i:s \G\M\T');
        $resource = "/{$this->bucket}/{$objectName}";
        $authHeader = $this->generateSignature("GET", "", "", $date, $resource);
        $url = "https://{$this->bucket}.{$this->endpoint}/{$objectName}";
        $range = filesize(file_exists($saveAsPath) ? $saveAsPath : 0);
        $headers = [
            'Authorization: ' . $authHeader,
            'Date: ' . $date,
            'Range: bytes=' . $range . '-'
        ];
        try {
            // 自行实现下载方法
            return true;
        } catch (Exception $e) {
            return false;
        }
    }
}